import pandas as pd
import numpy as np
import math
import matplotlib.pyplot as plt
import seaborn as sns
import scipy.optimize as opt
from sklearn.metrics import classification_report #这个包是评价报告 
plt.style.use('fivethirtyeight')

def filter(df):
    result = []
    for i in range(len(df)):
        if df[i][0] <= 1:
            result.append(df[i])
    return result
data_low = pd.read_csv('lbh_training_data_low.txt', names=['dim1', 'dim2', 'dim3','target'])
data_mid = pd.read_csv('lbh_training_data_mid.txt', names=['dim1', 'dim2', 'dim3','target'])
data_high = pd.read_csv('lbh_training_data_high.txt', names=['dim1', 'dim2', 'dim3','target'])
data_test = pd.read_csv('build_test_data.txt', names=['dim1', 'dim2', 'dim3','target'])
data_training_test = pd.read_csv('lbh_training_data_test.txt',names=['dim1', 'dim2', 'dim3','target'])

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# np.array(df.iloc[:, -1])
data_origin = data_test.iloc[:, :].values
data_origin = filter(data_origin)
# 将数据分为三部分
x_low, x_mid, x_high = [], [], []
y_low, y_mid, y_high = [], [], []
z_low, z_mid, z_high = [], [], []
# 打印表格
for i in range(len(data_origin)):
    print('%-10.3f' % data_origin[i][0], end=' ')
    print('%-10.3f' % data_origin[i][1], end=' ')
    print('%-10.3f' % data_origin[i][2], end=' ')
    print('%-6.3f' % (data_origin[i][3] / 100), end=' ')
    print()
ax = plt.subplot(111, projection='3d')  # 创建一个三维的绘图工程

for i in range(len(data_origin)):
    if data_origin[i][3] <= 20:
        x_low.append(data_origin[i][0])
        y_low.append(data_origin[i][1])
        z_low.append(data_origin[i][2])
    elif data_origin[i][3] >= 80:
        x_high.append(data_origin[i][0])
        y_high.append(data_origin[i][1])
        z_high.append(data_origin[i][2])
    else:
        x_mid.append(data_origin[i][0])
        y_mid.append(data_origin[i][1])
        z_mid.append(data_origin[i][2])
# 将数据点分成三部分画，在颜色上有区分度
ax.scatter(x_low, y_low, z_low, c='g', label='little')  # 绘制数据点
ax.scatter(x_mid, y_mid, z_mid, c='y', label='middle')
ax.scatter(x_high, y_high, z_high, c='r', label='higher')

ax.set_zlabel('Z')  # 坐标轴
ax.set_ylabel('Y')
ax.set_xlabel('X')
# plt.title('人工标签')
plt.legend()
plt.show()


def get_X(df):
# 读取特征
#     """
#     use concat to add intersect feature to avoid side effect
#     not efficient for big dataset though
#     """
    ones = pd.DataFrame({'ones': np.ones(len(df))}) # ones是m行1列的dataframe
    data = pd.concat([ones, df], axis=1)            # 合并数据，根据列合并
    return data.iloc[:, :-1].values                 # 这个操作返回 ndarray,不是矩阵


def get_y(df):#读取标签
#     '''assume the last column is the target'''
    return np.array(df.iloc[:, -1])                # df.iloc[:, -1]是指df的最后一列


X = get_X(data_low)
X_test = get_X(data_training_test)
# 获取数据格式
print(X.shape)
y_low = get_y(data_low)
y_mid = get_y(data_mid)
y_high = get_y(data_high)
y_test = get_y(data_test)
y_training_test = get_y(data_training_test)
print(y_training_test.shape)

## 实现sigmoid 函数
def sigmoid(z):
    return 1/(1+np.exp(-z))

theta = theta=np.zeros(4) # X(m*n) so theta is n*1
## 实现代价函数 cost
def cost(theta, X, y):
     return np.mean(-y * np.log(sigmoid(X @ theta)) - (1 - y) * np.log(1 - sigmoid(X @ theta)))                            ## 补充YOUR_CODE处的代码

# X @ theta与X.dot(theta)等价
cost_low  = cost(theta, X, y_low)
cost_mid = cost(theta, X, y_mid)
cost_high = cost(theta, X, y_high)
## 实现梯度计算函数 gradient
def gradient(theta, X, y):
    return (np.dot(X.T,(sigmoid(X@theta)-y)))/(len(X))
# 用牛顿法拟合参数
res_low = opt.minimize(fun=cost, x0=theta, args=(X, y_low), method='Newton-CG', jac=gradient)
res_mid = opt.minimize(fun=cost, x0=theta, args=(X, y_mid), method='Newton-CG', jac=gradient)
res_high = opt.minimize(fun=cost, x0=theta, args=(X, y_high), method='Newton-CG', jac=gradient)


# 预测结果
def cal_val(v_low, v_mid, v_high):
    if v_low >= max(v_mid, v_high):
        return 0
    elif v_mid >= max(v_low, v_high):
        return 50
    else:
        return 100


def predict_training(x, theta):
    prob = sigmoid(x @ theta.T)
    res = []
    for i in range(len(prob)):
        res.append(int(prob[i]))
    return res


def predict(x, theta_low, theta_mid, theta_high):
    prob_low = sigmoid(x @ theta_low.T)
    prob_mid = sigmoid(x @ theta_mid.T)
    prob_high = sigmoid(x @ theta_high.T)
    res = []
    for i in range(len(prob_low)):
        res.append(cal_val(prob_low[i], prob_mid[i], prob_high[i]))
    return res



final_theta_low = res_low.x
final_theta_mid = res_mid.x
final_theta_high = res_high.x

y_pred = predict(X, final_theta_low, final_theta_mid, final_theta_high)
# print(y_pred)
y_pred_test = predict(X_test,final_theta_low, final_theta_mid, final_theta_high)
print(y_pred_test)
cnt = 0


# 生成预测结果
# np.array(df.iloc[:, -1])
data_origin = filter(data_training_test.iloc[:,:].values)

x_low, x_mid, x_high = [],[],[]
y_low, y_mid, y_high = [],[],[]
z_low, z_mid, z_high = [], [], []
ax = plt.subplot(111, projection='3d') # 创建一个三维的绘图工程
# 将数据点分成三部分画，在颜色上有区分度
cnt = 0
for i in range(len(y_pred_test)):
    if y_pred_test[i] <=20:
        if data_origin[i][3] <=20:
            cnt += 1
        x_low.append(data_origin[i][0])
        y_low.append(data_origin[i][1])
        z_low.append(data_origin[i][2])
    elif y_pred_test[i] >= 80:
        if data_origin[i][3] >= 80:
            cnt += 1
        x_high.append(data_origin[i][0])
        y_high.append(data_origin[i][1])
        z_high.append(data_origin[i][2])
    else:
        if 20 <data_origin[i][3]<80 :
            cnt += 1
        x_mid.append(data_origin[i][0])
        y_mid.append(data_origin[i][1])
        z_mid.append(data_origin[i][2])
ax.scatter(x_low, y_low, z_low, c='g',label='little') # 绘制数据点
ax.scatter(x_mid, y_mid, z_mid, c='y',label='middle')
ax.scatter(x_high, y_high, z_high, c='r',label='higher')

ax.set_zlabel('Z') # 坐标轴
ax.set_ylabel('Y')
ax.set_xlabel('X')
# plt.title('人工标签')
plt.legend()
plt.show()
print(cnt/len(y_pred_test))
# 打印表格
for i in range(len(data_origin)):
    print('%-10.3f' % data_origin[i][0], end=' ')
    print('%-10.3f' % data_origin[i][1], end=' ')
    print('%-10.3f' % data_origin[i][2], end=' ')
    print('%-6.3f' % (y_pred_test[i]/100), end=' ')
    print()